package lt.ntec.danblog.server.core.model.gae;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.jdo.JDOException;
import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import lt.ntec.danblog.server.core.model.DAOException;
import lt.ntec.danblog.server.core.model.Tag;
import lt.ntec.danblog.server.core.model.TagDAO;
import lt.ntec.danblog.server.core.model.Taggable;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

public class JDOTagDAO extends JDOBaseDAO<Tag> implements TagDAO {

    public JDOTagDAO(PersistenceManager pm) {
        super(pm);
    }

    @Override
    public Class<? extends Tag> getEntityClass() {
        return JDOTag.class;
    }

    @Override
    public Tag getById(Long id) {
        Tag entity = null;
        try {
            entity = pm.getObjectById(getEntityClass(), id);
            JDOTag tag = (JDOTag) entity;
            HashSet<Taggable> parents = new HashSet<Taggable>();
            for (JDORelationMap key : tag.getParentMap()) {
                //System.err.println(key.getKey().getKind());
                //System.err.println(key.getFullClassName());
                parents.add((Taggable) pm.getObjectById(
                        Class.forName(key.getFullClassName()), key.getKey()));
            }
            tag.setParents(parents);
        } catch (JDOObjectNotFoundException e) {
            throw new DAOException(e);
        } catch (ClassNotFoundException e) {
            throw new DAOException(e);
        }
        return entity;
    }

    @Override
    public Long save(Tag entity) {
        try {
            pm.makePersistent(entity);
            //TODO turn on parent saving
            //pm.makePersistentAll(entity.getEntryList());
        } catch (JDOException e) {
            throw new DAOException(e);
        }
        return entity.getId();
    }

    @SuppressWarnings("unchecked")
    @Override
    public Tag getTagByValue(String value) {
        Query query = pm.newQuery(getEntityClass());
        query.setFilter("value == valueParam");
        query.setOrdering("value asc");
        query.declareParameters("String valueParam");
        List<Tag> results = (List<Tag>) query.execute(value);
        if (results.isEmpty()) {
            return null;
        }
        return results.get(0);
    }

    @SuppressWarnings("unchecked")
    @Override
    public void deleteParentFromTags(Taggable entity) {
        Key k = KeyFactory.createKey(entity.getClass().getSimpleName(),
                entity.getId());
        JDORelationMap m = new JDORelationMap(entity.getClass().getName(), k);
        // System.err.println(entity.getClass().getSimpleName());
        Query query = pm.newQuery(getEntityClass());
        query.setFilter("parentKeys == valueParam");
        query.declareImports("import com.google.appengine.api.datastore.Key");
        query.declareParameters("Key valueParam");
        List<Tag> results;
        try {
            results = (List<Tag>) query.execute(k);
        } catch (Exception e) {
            throw new DAOException(e);
        }
        for (Tag tag : results) {
            ((JDOTag) tag).getParentKeys().remove(k);
            ((JDOTag) tag).getParentMap().remove(m);
        }
        pm.makePersistentAll(results);
    }

    @SuppressWarnings("unchecked")
    @Override
    public Set<? extends Tag> getTagsByParent(Taggable entity) {
        Key k = KeyFactory.createKey(entity.getClass().getSimpleName(),
                entity.getId());

        String queryStr = "select from " + getEntityClass().getName()
                + " where parentKeys == :p1";
        List<Tag> results = (List<Tag>) pm.newQuery(queryStr).execute(k);
        return new TreeSet<Tag>(results);
    }

    @Override
    public Tag saveUniqueTag(Tag entity) {
        Tag tag = getTagByValue(entity.getValue());
        if (tag == null) {
            save(entity);
            return entity;
        }
        return tag;
    }

    @Override
    public Map<Class<?>, Set<Long>> getParentsByTagId(Long id) {
        JDOTag tag = (JDOTag) getById(id);
        Map<Class<?>, Set<Long>> parents = new HashMap<Class<?>, Set<Long>>();

        try {
            for (JDORelationMap key : tag.getParentMap()) {
                if (parents.get(key) == null) {
                    parents.put(Class.forName(key.getFullClassName()),
                            new TreeSet<Long>());
                }
                parents.get(Class.forName(key.getFullClassName())).add(
                        key.getKey().getId());

            }
        } catch (ClassNotFoundException e) {
            throw new DAOException(e);
        }
        return parents;
    }
}
